using System;

namespace EstimationStudio.Core
{
	/// <summary>
	///  The <see cref="Formulas"/> class holds helper methods that perform estimation related calculations.
	/// </summary>
	public class Formulas
	{
		/// <summary>
		/// Calculates the expected case of an estimation when given a range of best, most likely, and worst cases.
		/// </summary>
		/// <param name="bestCase">The best possible outcome.</param>
		/// <param name="mostLikelyCase">The most likely outcome.</param>
		/// <remarks>ExpectedCase is used because it weights the most likely case to provide a more accurate expected case.</remarks>
		/// <param name="worstCase">The worst possible outcome.</param>
		/// <returns>A single value representing the expected case of an estimation.</returns>
		public Double ExpectedCase(Double bestCase, Double mostLikelyCase, Double worstCase)
		{
			return Math.Round((bestCase + (mostLikelyCase * 4) + worstCase) / 6, 2);
		}

		/// <summary>
		///  Calculates the overall influences a rating has based on the individual values of the rating.
		/// </summary>
		/// <param name="rating">Specified rating to calculate the influence from.</param>
		/// <returns>The calculated influence of the given rating.</returns>
		public Double Influence (AdjustmentVariability rating)
		{
			// The influence only uses the very smallest and very biggest values.
			// Walk through each of the individual values marking smallest and largest values
			//  to be used in the influence calculation.
			Double smallestValue = 1.0, largestValue = 1.0;

			if (!Double.IsNaN(rating[Rating.VeryLow]))
			{
				if (rating[Rating.VeryLow] < smallestValue)
					smallestValue = rating[Rating.VeryLow];

				if (rating[Rating.VeryLow] > largestValue)
					largestValue = rating[Rating.VeryLow];
			}

			if (!Double.IsNaN(rating[Rating.Low]))
			{
				if (rating[Rating.Low] < smallestValue)
					smallestValue = rating[Rating.Low];

				if (rating[Rating.Low] > largestValue)
					largestValue = rating[Rating.Low];
			}

			if (!Double.IsNaN(rating[Rating.High]))
			{
				if (rating[Rating.High] < smallestValue)
					smallestValue = rating[Rating.High];

				if (rating[Rating.High] > largestValue)
					largestValue = rating[Rating.High];
			}

			if (!Double.IsNaN(rating[Rating.VeryHigh]))
			{
				if (rating[Rating.VeryHigh] < smallestValue)
					smallestValue = rating[Rating.VeryHigh];

				if (rating[Rating.VeryHigh] > largestValue)
					largestValue = rating[Rating.VeryHigh];
			}

			if (!Double.IsNaN(rating[Rating.ExtraHigh]))
			{
				if (rating[Rating.ExtraHigh] < smallestValue)
					smallestValue = rating[Rating.ExtraHigh];

				if (rating[Rating.ExtraHigh] > largestValue)
					largestValue = rating[Rating.ExtraHigh];
			}

			return Math.Round(largestValue / smallestValue, 2);
		}

		/// <summary>
		///  Calculates the overall error variability of an estimate by looking at the estimates expected case
		///   against the specified project phase's error variability.
		/// </summary>
		/// <param name="estimate">Any estimate that has at least one feature.</param>
		/// <param name="projectPhase">The current project phase to calculate the error variability with.</param>
		/// <returns></returns>
		public ErrorVariability ErrorVariability (Estimate estimate, ProjectPhase projectPhase)
		{
			if (estimate == null)
				throw new ArgumentNullException("estimate");

			if (projectPhase == null)
				throw new ArgumentNullException("projectPhase");

			return projectPhase.ErrorVariability * estimate.ExpectedCase.Nominal;
		}
	}
}